import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class LevelGenerator {

	protected JFrame mFrame;

	protected JPanel mMapPanel = new JPanel();

	protected Level mLevel = new Level(20, 20);

	protected JTextField mPacManSpeedField = new JTextField("40");

	protected JTextField mGhostNormalSpeedField = new JTextField("40");

	protected JTextField mGhostVulnerableSpeedField = new JTextField("30");

	protected JTextField mGhostIntelligenceField = new JTextField("2");

	protected JTextField mPowerBallDurationField = new JTextField("5000");

	public LevelGenerator() {
		this.createGUI();
		this.createMap();
	}

	public void loadNewMap(String aSizePattern) {
		String[] sizes = aSizePattern.split(",");
		if (sizes.length != 2) {
			JOptionPane.showMessageDialog(null, "Taille invalide!");
		} else {
			int sizeX = 0;
			int sizeY = 0;
			try {
				sizeX = Integer.parseInt(sizes[0]);
				sizeY = Integer.parseInt(sizes[1]);
			} catch (NumberFormatException nfe) {
				nfe.printStackTrace();
			}

			if (sizeX > 0 && sizeY > 0) {
				this.changeLevel(new Level(sizeX, sizeY));
			} else {
				JOptionPane.showMessageDialog(null, "Taille invalide!");
			}
		}
	}

	public void createMap() {
		String size = JOptionPane.showInputDialog(null,
				"Taille de la map? ('x,y')");

		// Si une taille à été renseignée
		if (size != null) {

			// Si la taille n'a pas été renseignée.
			if (size.isEmpty()) {
				// Taille par defaut
				size = "20,20";
			}

			// Ouverture d'un nouveau niveau
			loadNewMap(size);
		}
	}

	public void changeLevel(Level aNewLevel) {

		// Changement de la map
		mMapPanel.remove(this.mLevel);
		aNewLevel.getMap();
		this.mLevel = aNewLevel;
		mMapPanel.add(this.mLevel);

		// Changement des valeurs des paramètres
		mPacManSpeedField.setText(this.mLevel.getPacManSpeedField().toString());
		mGhostNormalSpeedField.setText(this.mLevel.getGhostNormalSpeedField()
				.toString());
		mGhostVulnerableSpeedField.setText(this.mLevel
				.getGhostVulnerableSpeedField().toString());
		mGhostIntelligenceField.setText(this.mLevel.getGhostIntelligence()
				.toString());
		mPowerBallDurationField.setText(this.mLevel.getPowerBallDurationField()
				.toString());

		this.refreshIHM();
	}

	public void loadFile() {
		LevelFileLoader loader = new LevelFileLoader();
		// Si le fichier a été chargé
		if (loader.loadFile()) {
			this.changeLevel(loader.getLoadedLevel());
		} else {
			JOptionPane.showMessageDialog(null,
					"Impossible de charger le niveau");
		}
	}

	private void createGUI() {

		// Création de la fenetre principale
		mFrame = new JFrame("PacMan Level Generator");
		mFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		mFrame.setBackground(Color.lightGray);

		// Création de la partie avec les boutons
		JPanel buttonsPanel = this.getButtonsPanel();
		mFrame.add(buttonsPanel, BorderLayout.NORTH);

		// Création de la partie map
		JScrollPane scrollPane = new JScrollPane();
		scrollPane.getViewport().add(mMapPanel);
		scrollPane.setBorder(BorderFactory.createLineBorder(Color.blue, 2));
		// scrollPane.setPreferredSize(new Dimension(100,100));
		mMapPanel.add(this.mLevel);
		mFrame.add(scrollPane, BorderLayout.CENTER);

		// Création de la partie paramètre
		JPanel parameterArea = new JPanel(new GridLayout(2, 1));
		JPanel labelArea = new JPanel(new GridLayout(1, 5));
		JPanel fieldsArea = new JPanel(new GridLayout(1, 5));

		mPacManSpeedField.setPreferredSize(new Dimension(100, 20));
		mGhostNormalSpeedField.setPreferredSize(new Dimension(100, 20));
		mGhostVulnerableSpeedField.setPreferredSize(new Dimension(100, 20));
		mGhostIntelligenceField.setPreferredSize(new Dimension(100, 20));
		mPowerBallDurationField.setPreferredSize(new Dimension(100, 20));

		labelArea.add(new JLabel("Vitesse PacMan"));
		fieldsArea.add(mPacManSpeedField);

		labelArea.add(new JLabel("Vitesse fantômes (actifs)"));
		fieldsArea.add(mGhostNormalSpeedField);

		labelArea.add(new JLabel("Vitesse fantômes (vulnérable)"));
		fieldsArea.add(mGhostVulnerableSpeedField);

		labelArea.add(new JLabel("Intellignece fantomes"));
		fieldsArea.add(mGhostIntelligenceField);

		labelArea.add(new JLabel("Durée PowerBalls"));
		fieldsArea.add(mPowerBallDurationField);

		parameterArea.add(labelArea);
		parameterArea.add(fieldsArea);
		mFrame.add(parameterArea, BorderLayout.SOUTH);

		// Création de la partie légende
		JPanel legendArea = new JPanel(new GridLayout(15, 1));
		legendArea.add(new LegendPanel(0, "Normal"));
		legendArea.add(new LegendPanel(1, "Mur"));
		legendArea.add(new LegendPanel(2, "PowerBall"));
		legendArea.add(new LegendPanel(3, "PacMan"));
		legendArea.add(new LegendPanel(4, "Barrière"));
		legendArea.add(new LegendPanel(5, "Fantome"));
		legendArea.add(new LegendPanel(-1, "Téléporteur 1"));
		legendArea.add(new LegendPanel(-2, "Téléporteur 2"));
		legendArea.add(new LegendPanel(-3, "Téléporteur 3"));
		legendArea.add(new LegendPanel(-4, "Téléporteur 4"));
		legendArea.add(new LegendPanel(-10, "Erreur!"));

		mFrame.add(legendArea, BorderLayout.EAST);

		this.addMenu();
	}

	public JPanel getButtonsPanel() {

		JPanel buttonsPanel = new JPanel();
		JButton generateButton = new JButton("Generate!");
		generateButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				LevelGenerator.this.generateFile();
			}
		});

		JButton addRowButton = new JButton("+ row");
		addRowButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				LevelGenerator.this.addRow();
			}
		});

		JButton addLineButton = new JButton("+ line");
		addLineButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				LevelGenerator.this.addLine();
			}
		});

		JButton removeRowButton = new JButton("- row");
		removeRowButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				LevelGenerator.this.removeRow();
			}
		});

		JButton removeLineButton = new JButton("- line");
		removeLineButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				LevelGenerator.this.removeLine();
			}
		});

		buttonsPanel.add(generateButton);
		buttonsPanel.add(addRowButton);
		buttonsPanel.add(addLineButton);
		buttonsPanel.add(removeRowButton);
		buttonsPanel.add(removeLineButton);

		return buttonsPanel;
	}

	public void launch() {

		SwingUtilities.invokeLater(new Runnable() {
			public void run() {

				JFrame frame = LevelGenerator.this.mFrame;
				Dimension screenSize = java.awt.Toolkit.getDefaultToolkit()
						.getScreenSize();

				LevelGenerator.this.refreshIHM();

				frame.setLocation((screenSize.width - frame.getWidth()) / 2,
						(screenSize.height - frame.getHeight()) / 2);

				LevelGenerator.this.mFrame.setVisible(true);

			}
		});

	}

	protected Integer extractValue(String aValue, Integer aDefaultValue) {
		int extractedValue = aDefaultValue;

		try {
			extractedValue = Integer.parseInt(aValue);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return extractedValue;
	}

	public void generateFile() {

		String name = JOptionPane.showInputDialog(null, "Nom du fichier?");
		if (name != null) {

			if (name.isEmpty()) {
				name = "NewLevel";
			}
			LevelFileWriter writer = new LevelFileWriter(name);

			List<List<LevelCase>> levelMap = this.mLevel.getMap();

			// Ecriture de la taille de la map
			writer.print(levelMap.get(0).size() + " ");
			writer.print(levelMap.size() + " ");

			// Extraction des paramètres de la map
			Integer pacmanSpeed = this.extractValue(
					mPacManSpeedField.getText(), 800);
			Integer ghostNormalSpeed = this.extractValue(
					mGhostNormalSpeedField.getText(), 800);
			Integer ghostVulnerableSpeed = this.extractValue(
					mGhostVulnerableSpeedField.getText(), 1000);
			Integer ghostIntelligence = this.extractValue(
					mGhostIntelligenceField.getText(), 2);
			Integer powerballDuration = this.extractValue(
					mPowerBallDurationField.getText(), 10000);

			// Ecriture des paramètres de la map
			writer.print(pacmanSpeed + " ");
			writer.print(ghostNormalSpeed + " ");
			writer.print(ghostVulnerableSpeed + " ");
			writer.print(ghostIntelligence + " ");
			writer.print(powerballDuration + " ");

			writer.printNewLine();

			// Parcours de toutes les lignes dans l'ordre inverse
			List<LevelCase> currentLine;
			Iterator<LevelCase> caseIt;
			LevelCase currentCase;

			for (int lineIndex = levelMap.size() - 1; lineIndex >= 0; lineIndex--) {
				currentLine = levelMap.get(lineIndex);

				// Parcours de toutes les cases
				caseIt = currentLine.iterator();
				while (caseIt.hasNext()) {
					currentCase = caseIt.next();

					// Ajout de la valeur de la case
					writer.print(currentCase.getValue().toString());

					// Ajout d'un espace si ce n'est pas la dernière valeur
					if (caseIt.hasNext()) {
						writer.print(" ");
					}

				}

				// Passage a la ligne si ce n'est pas la dernière ligne
				if (lineIndex != 0) {
					writer.printNewLine();
				}

			}

			writer.closeStream();

			JOptionPane.showMessageDialog(mFrame, "Génération réussie!");
		}
	}

	public void addMenu() {
		JMenuBar menubar = new JMenuBar();

		JMenu file = new JMenu("File");

		JMenuItem newMenuItem = new JMenuItem("New Map");
		newMenuItem.setToolTipText("Construit une nouvelle map");
		newMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				LevelGenerator.this.createMap();
			}
		});
		file.add(newMenuItem);

		JMenuItem loadMenuItem = new JMenuItem("Load File");
		loadMenuItem.setToolTipText("Charge une map");
		loadMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				LevelGenerator.this.loadFile();
			}
		});
		file.add(loadMenuItem);

		JMenuItem saveMenuItem = new JMenuItem("Save File");
		saveMenuItem.setToolTipText("Sauvegarde la map");
		saveMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				LevelGenerator.this.generateFile();
			}
		});
		file.add(saveMenuItem);

		JMenuItem exitMenuItem = new JMenuItem("Exit");
		exitMenuItem.setToolTipText("Quitte l'application");
		exitMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				System.exit(0);
			}
		});

		file.add(exitMenuItem);
		menubar.add(file);

		JMenu map = new JMenu("Map");

		JMenuItem addRowMenuItem = new JMenuItem("Add Row");
		addRowMenuItem.setToolTipText("Ajoute une colonne");
		addRowMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				LevelGenerator.this.addRow();
			}
		});
		map.add(addRowMenuItem);

		JMenuItem addLineMenuItem = new JMenuItem("Add Line");
		addLineMenuItem.setToolTipText("Ajoute une ligne");
		addLineMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				LevelGenerator.this.addLine();
			}
		});
		map.add(addLineMenuItem);

		JMenuItem removeRowMenuItem = new JMenuItem("Remove row");
		removeRowMenuItem.setToolTipText("Supprime une ligne");
		removeRowMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				LevelGenerator.this.removeRow();
			}
		});
		map.add(removeRowMenuItem);

		JMenuItem removeLineMenuItem = new JMenuItem("Remove line");
		removeLineMenuItem.setToolTipText("Supprime une colonne");
		removeLineMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				LevelGenerator.this.removeLine();
			}
		});
		map.add(removeLineMenuItem);

		JMenu credits = new JMenu("?");

		JMenuItem creditMenuItem = new JMenuItem("Credits");
		creditMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				LevelGenerator.this.showCredits();
			}
		});
		credits.add(creditMenuItem);

		menubar.add(file);
		menubar.add(map);
		menubar.add(credits);

		this.mFrame.setJMenuBar(menubar);
	}

	public void showCredits() {
		JOptionPane.showMessageDialog(null, "Made by KaL \n		M2-TIIL 2013");
	}

	public void addRow() {
		if (this.mLevel.getSizeX() < 42) {
			this.mLevel.addRow();
			this.refreshIHM();
		}
	}

	public void addLine() {
		if (this.mLevel.getSizeY() < 42) {
			this.mLevel.addLine();
			this.refreshIHM();
		}
	}

	public void removeRow() {
		this.mLevel.removeRow();
		this.refreshIHM();
	}

	public void removeLine() {
		this.mLevel.removeLine();
		this.refreshIHM();
	}

	public void refreshIHM() {
		int sizeX = Math.max(200, (this.mLevel.getSizeX() * 20) + 200);
		sizeX = Math.min(sizeX, 800);

		int sizeY = Math.max(200, (this.mLevel.getSizeY() * 20) + 150);
		sizeY = Math.min(sizeY, 800);

		mFrame.setPreferredSize(new Dimension(sizeX, sizeY));
		this.mFrame.pack();
	}

	/**
	 * Classe interne pour afficher la legend de la map.
	 */
	private class LegendPanel extends JPanel {
		private static final long serialVersionUID = -9106985567015267176L;

		public LegendPanel(Integer aCaseValue, String aText) {
			this.add(new LevelCase(aCaseValue, false));
			this.add(new JLabel(aText));
			this.setPreferredSize(new Dimension(150, 20));
		}
	}
}
